package com.gopay.common.rcs.service.antiphish.check.impl;

import com.gopay.common.constants.proccode.ProcCodeEnum;
import com.gopay.common.constants.trans.GatewayChannel;
import com.gopay.common.constants.txncd.MerTxnCd;
import com.gopay.common.domain.cust.CustCorpInfo;
import com.gopay.common.domain.rcs.RcsAntiPhishViolateLog;
import com.gopay.common.domain.rcs.req.RcsAntiPhishingRequest;
import com.gopay.common.domain.rcs.res.RcsAntiPhishingResponse;
import com.gopay.common.exception.GopayException;
import com.gopay.common.rcs.bean.*;
import com.gopay.common.rcs.manager.antiphish.RcsAntiPhishingCustManager;
import com.gopay.common.rcs.manager.antiphish.RcsAntiPhishingGlobalManager;
import com.gopay.common.rcs.manager.antiphish.RcsAntiPhishingViolateLogManager;
import com.gopay.common.rcs.service.antiphish.check.RcsAntiPhishingCheckService;
import com.gopay.common.rcs.service.antiphish.manager.RcsAntiPhishingManagerCustService;
import com.gopay.common.rcs.utils.RcsUtil;
import com.gopay.common.rcs.vo.RcsAntiPhishConfCustlVo;
import com.gopay.common.user.manager.CustCorpInfoQueryManager;
import com.gopay.common.util.FileUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

@Repository
public class RcsAntiPhishingCheckServiceImpl implements RcsAntiPhishingCheckService {
    protected final static Log logger = LogFactory.getLog(RcsAntiPhishingCheckServiceImpl.class);
    @Autowired
    protected CustCorpInfoQueryManager custCorpInfoQueryManager;
    @Autowired
    protected RcsAntiPhishingViolateLogManager rcsAntiPhishingViolateLogManager;
    @Autowired
    protected RcsAntiPhishingGlobalManager rcsAntiPhishingGlobalManager;
    @Autowired
    protected RcsAntiPhishingCustManager rcsAntiPhishingCustManager;
    
    @Resource(name = "rcsAntiPhishingManagerCustService")
    protected RcsAntiPhishingManagerCustService rcsAntiPhishingManagerCustService;

    @Override
    public RcsAntiPhishingResponse rcsAntiPhishingCheck(RcsAntiPhishingRequest req) throws GopayException {
        logger.warn("反钓鱼校验.BEGIN." + req);
        CustCorpInfo cust = custCorpInfoQueryManager.get(req.getCustId());
        // 域名校验
        RcsAntiPhishingResponse resp=new RcsAntiPhishingResponse();
        //后台API因为没有RefererDomain，所以不做域名校验  edit by fanghw 20170707
        if(StringUtils.isNotBlank(req.getRedirectType())&&
                (req.getRedirectType().contains("SELF")||req.getRedirectType().contains("QUERY"))){
            //后台API
            resp.setProcCode(ProcCodeEnum.PROC_CODE_200S1000.getCode());
        }else{
            //浏览器重定向
            resp = doDomainnameCheck(cust, req);
        }




        if (!ProcCodeEnum.PROC_CODE_200S1000.getCode().equals(resp.getProcCode())) {
            return resp;
        }
        //生产bug7期 时间戳移动到外部 timestampCheckCommander 做独立 命令 校验 lx 2017-07-20 14:06:35
        // 时间戳校验
        /*resp = doTimestampCheck(cust, req);
        if (!ProcCodeEnum.PROC_CODE_200S1000.getCode().equals(resp.getProcCode())) return resp;*/
        // 订单IP校验
        if(!req.isDisableIpCheck()) {
            resp = doOrderIPCheck(cust, req);
        }
        return resp;
    }
    
   /**
    * ip白名单+时间戳校验 add by dongdh 20160612  快捷（代扣）api校验商户IP
    * @param req
    * @return
    * @throws GopayException
    */
    @Override
    public RcsAntiPhishingResponse rcsIPWhiteCheck(RcsAntiPhishingRequest req) throws GopayException {
        logger.warn("ip白名单 反钓鱼校验.BEGIN." + req);
        CustCorpInfo cust = custCorpInfoQueryManager.get(req.getCustId());
        
//        RcsAntiPhishingResponse resp = new RcsAntiPhishingResponse(ProcCodeEnum.PROC_CODE_200S1000.getCode());
        // 时间戳校验
        //生产bug7期 时间戳移动到外部 timestampCheckCommander 做独立 命令 校验 lx 2017-07-20 14:06:35
      /*  resp = doTimestampCheck(cust, req);
        
        if (!ProcCodeEnum.PROC_CODE_200S1000.getCode().equals(resp.getProcCode())) return resp;*/
        // 白名单校验
        RcsAntiPhishingResponse resp = doIPWhiteCheck(cust, req);
        return resp;
    }
    
    /**
     * IP白名单校验 add by dongdh 20160612 快捷（代扣）api校验商户IP
     * @param cust
     * @param req
     * @return
     */
    public RcsAntiPhishingResponse doIPWhiteCheck(CustCorpInfo cust, RcsAntiPhishingRequest req) {
        
        logger.warn("开始Ip白名单校验." + req);
        
        //OrderIPCheckStatus status = rcsAntiPhishingGlobalManager.getOrderIPCheckStatus(cust.getCustId());
        //logger.warn("订单IP校验开关:" + status);
        //*******不获取开关    必须开启校验*************
        RcsAntiPhishingResponse resp = new RcsAntiPhishingResponse(ProcCodeEnum.PROC_CODE_200S1000.getCode());
        String ipOfRequest = req.getHeaderIP();
        
        RcsAntiPhishViolateLog log = null;
        
        RcsAntiPhishConfCustlVo vo = rcsAntiPhishingManagerCustService.getCustConfBySql(cust.getCustId());
        
        boolean boo = false;
        
        if(StringUtils.isNotBlank(vo.getReqSourceIp())){
            String[] ipList = null;
            if(vo.getReqSourceIp().indexOf(";")>-1){
                ipList = vo.getReqSourceIp().split(";");
            }else{
                ipList = new String[]{vo.getReqSourceIp()};
            }
            boolean match = false;
            for(int i=0;i<ipList.length; i++){
                if(ipOfRequest.equals(ipList[i])){
                    match = true;
                    break;
                }
            }
            boo = match;
        }
        
        //未配置  或者校验 不通过  都拒绝交易。
        if (!boo) {
            resp.setDicision(UserDicision.STOP.code);
            log = saveAntiPhishingRecordLog(AntiPhishType.IP_WHITE, cust, req, ipOfRequest, vo.getReqSourceIp(),
                    IllegalReqDicision.REJECTION, null);
            resp.setProcCode(ProcCodeEnum.PROC_CODE_200E5004.getCode());
            resp.setRickPhishingLogSq(log.getInfoSq());
        }
        
        logger.warn("结束Ip白名单校验." + resp);
        return  resp;
    }
    

    @Override
    public RcsAntiPhishingResponse doOrderIPCheck(RcsAntiPhishingRequest req) throws GopayException {
        CustCorpInfo cust = custCorpInfoQueryManager.get(req.getCustId());
        return doOrderIPCheck(cust, req);
    }

    @Override
    public RcsAntiPhishingResponse doOrderIPCheck(CustCorpInfo cust, RcsAntiPhishingRequest req) {
        logger.warn("开始订单IP校验." + req);
        OrderIPCheckStatus status = rcsAntiPhishingGlobalManager.getOrderIPCheckStatus(cust.getCustId());
        logger.warn("订单IP校验开关:" + status);
        RcsAntiPhishingResponse resp = new RcsAntiPhishingResponse(ProcCodeEnum.PROC_CODE_200S1000.getCode());
        if (status.isOn()) {
            String ipOfRequest = req.getHeaderIP();
            String ip = req.getTranIP();
            RcsAntiPhishViolateLog log = null;
            if (!ip.equals(ipOfRequest)) {
                logger.error("RcsAntiPhishingCheckService-反钓鱼-订单IP校验失败！！！报文中的IP:" + ip + ", HTTP来源IP:" + ipOfRequest
                        + ", 商户订单号:" + req.getMerOrderId());
                IllegalReqDicision ird = getIllegalReqDicision(cust.getCustId(), AntiPhishType.TRAN_IP);
                
                if (IllegalReqDicision.REJECTION.equals(status.getIllegalReqDicision())) {
                    resp.setDicision(UserDicision.STOP.code);
                    log = saveAntiPhishingRecordLog(AntiPhishType.TRAN_IP, cust, req, ipOfRequest, ip,
                            IllegalReqDicision.REJECTION, null);
                } else {
                    resp.setDicision(UserDicision.CONTINUE.code);
                    log = saveAntiPhishingRecordLog(AntiPhishType.TRAN_IP, cust, req, ipOfRequest, ip, ird,
                            UserDicision.UNHANDLE);
                }
                resp.setProcCode(ProcCodeEnum.PROC_CODE_200E5004.getCode());
                resp.setRickPhishingLogSq(log.getInfoSq());
            }
            //付款交易IP验证
            if(req.getTranCode().equals(MerTxnCd._4030.value)){//防钓鱼校验不做ip备案校验，4025付款到银行申请已移到它出，4030待后续移出 by lizheng 20160704
            	logger.warn("付款交易IP验证开始");
            	RcsAntiPhishConfCustlVo vo = rcsAntiPhishingManagerCustService.getCustConfBySql(cust.getCustId());
            	if(StringUtils.isNotBlank(vo.getReqSourceIp())){
            		String[] ipList = null;
            		if(vo.getReqSourceIp().indexOf(";")>-1){
            			ipList = vo.getReqSourceIp().split(";");
            		}else{
            			ipList = new String[]{vo.getReqSourceIp()};
            		}
            		boolean match = false;
            		for(int i=0;i<ipList.length; i++){
            			if(ipOfRequest.equals(ipList[i])){
            				match = true;
            				break;
            			}
            		}
            		if(!match){
            			 resp.setDicision(UserDicision.STOP.code);
                         log = saveAntiPhishingRecordLog(AntiPhishType.TRAN_IP, cust, req, ipOfRequest, ip,
                                 IllegalReqDicision.REJECTION, null);
                         resp.setProcCode(ProcCodeEnum.PROC_CODE_200E5004.getCode());
                         resp.setRickPhishingLogSq(log.getInfoSq());
            		}

            	}
            	logger.warn("付款交易IP验证结束");
            }
           
                
	            
            }
        
        return resp;
    }
    
    @Override
    public RcsAntiPhishingResponse doTimestampCheck(CustCorpInfo cust, RcsAntiPhishingRequest req)
            throws GopayException {
        logger.warn("开始时间戳校验." + req);
        TimestampCheckStatus status = rcsAntiPhishingGlobalManager.getTimestampCheckStatus(cust.getCustId());
        logger.warn("时间戳校验开关:" + status);
        RcsAntiPhishingResponse resp = new RcsAntiPhishingResponse(ProcCodeEnum.PROC_CODE_200S1000.getCode());
        if (!status.isOn()) {
            return resp;
        }
        long timeOfRequest = getTranDatetime(req.getGopayServerTime());
        long time = System.currentTimeMillis();
        long interval = 1000 * (status.getLegalValue());
        long actualValue = time - timeOfRequest;
        if (actualValue > interval) {
            logger.error("RcsAntiPhishingCheckService-反钓鱼-时间戳校验失败！！！当前时间-发起时间>最大差值. " + time + "-" + timeOfRequest
                    + "=" + (time - timeOfRequest) + ">" + interval + ", 商户订单号:" + req.getMerOrderId());
            IllegalReqDicision ird = getIllegalReqDicision(cust.getCustId(), AntiPhishType.TIMESTAMP);
            RcsAntiPhishViolateLog log = saveAntiPhishingRecordLog(AntiPhishType.TIMESTAMP, cust, req,
                    actualValue + "", interval + "", ird, null);
            resp.setProcCode(ProcCodeEnum.PROC_CODE_200E5003.getCode());
            resp.setRickPhishingLogSq(log.getInfoSq());
            resp.setDicision(UserDicision.STOP.code);
        }
        return resp;
    }

    private static long getTranDatetime(String s) throws GopayException {
        Date d = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            sdf.setLenient(false);
            d = sdf.parse(s);
        } catch (Exception e) {
            logger.info("反钓鱼-时间戳校验失败，时间戳格式错误:" + s);
            throw new GopayException(ProcCodeEnum.PROC_CODE_200E1015, "反钓鱼-时间戳校验失败，时间戳格式错误:" + s);
        }
        return d.getTime();
    }

    @Override
    public VerifyCodeCheckStatus verifyCodeConfig(String custId) throws GopayException {
        return rcsAntiPhishingGlobalManager.verifyCodeConfig(custId);
    }

    @Override
    public RcsAntiPhishingResponse doDomainnameCheck(RcsAntiPhishingRequest req) throws GopayException {
        CustCorpInfo cust = custCorpInfoQueryManager.get(req.getCustId());
        return doDomainnameCheck(cust, req);
    }

    @Override
    public RcsAntiPhishingResponse doDomainnameCheck(CustCorpInfo cust, RcsAntiPhishingRequest req)
            throws GopayException {
        logger.warn("开始域名校验." + req);
        DomainnameCheckStatus status = rcsAntiPhishingGlobalManager.getDomainnameCheckStatus(cust.getCustId());
        logger.warn("域名校验开关:" + status);
        RcsAntiPhishingResponse resp = new RcsAntiPhishingResponse(ProcCodeEnum.PROC_CODE_200S1000.getCode());
        if (!status.isOn()) {
            return resp;
        }
        String domainnameOfRequest = req.getRefererDomain();
        String domainnameList = status.getLegalValue();
        if (null != domainnameList && null != domainnameOfRequest) {
            String[] list = domainnameList.split(DELIMITER_DOMAINNAME_LIST);
            RcsAntiPhishingResponse r = null;
            if (status.isFuzzyOn()) {// 模糊域名匹配
                IllegalReqDicision ird = getIllegalReqDicision(cust.getCustId(), AntiPhishType.DOMAINNAME_FUZZY);
                r = checkDomainnameCheckStatusFuzzy(list, domainnameOfRequest, cust, req, ird);
            } else {// 精确域名匹配
                IllegalReqDicision ird = getIllegalReqDicision(cust.getCustId(), AntiPhishType.DOMAINNAME_EXACT);
                r = checkDomainnameCheckStatusExact(list, domainnameOfRequest, cust, req, ird);
            }
            if (null != r) {
                return r;
            }
        }
        return resp;
    }

    private IllegalReqDicision getIllegalReqDicision(String custId, AntiPhishType type) {
        switch (type) {
        case DOMAINNAME_EXACT:
            return IllegalReqDicision.REJECTION;
        case DOMAINNAME_FUZZY:
            return IllegalReqDicision.REJECTION;
        case TIMESTAMP:
            return IllegalReqDicision.REJECTION;
        case TRAN_IP:
            return IllegalReqDicision.USER_DECIDE;
        case IP_WHITE : 
            return IllegalReqDicision.REJECTION; //ip 白名单 拒绝交易 
        default:
            throw new RuntimeException("wrong AntiPhishType!" + custId + ", " + type);
        }
    }

    private RcsAntiPhishingResponse checkDomainnameCheckStatusExact(String[] list, String domainnameOfRequest,
            CustCorpInfo cust, RcsAntiPhishingRequest req, IllegalReqDicision ird) {
        RcsAntiPhishingResponse resp = new RcsAntiPhishingResponse();
        boolean checkOK = false;
        for (int i = 0; i < list.length; i++) {
            if (domainnameOfRequest.toLowerCase().equals(list[i].toLowerCase())) {
                checkOK = true;
                break;
            }
        }
        if (!checkOK) {
            RcsAntiPhishViolateLog log = saveAntiPhishingRecordLog(AntiPhishType.DOMAINNAME_EXACT, cust, req,
                    domainnameOfRequest, StringUtils.join(list, DELIMITER_DOMAINNAME_LIST), ird, null);
            logger.error("RcsAntiPhishingCheckService-反钓鱼-域名精确匹配校验失败！！！报文中的域名:" + domainnameOfRequest + ", 合法域名列表:"
                    + StringUtils.join(list, ",") + ", 商户订单号:" + req.getMerOrderId());
            resp.setProcCode(ProcCodeEnum.PROC_CODE_200E5001.getCode());
            resp.setRickPhishingLogSq(log.getInfoSq());
            resp.setDicision(UserDicision.STOP.code);
            return resp;
        }
        return null;
    }

    private RcsAntiPhishingResponse checkDomainnameCheckStatusFuzzy(String[] list, String domainnameOfRequest,
            CustCorpInfo cust, RcsAntiPhishingRequest req, IllegalReqDicision ird) {
        RcsAntiPhishingResponse resp = new RcsAntiPhishingResponse();
        boolean checkOK = false;
        for (int i = 0; i < list.length; i++) {
            if (RcsUtil.isDomainnameFuzzyMatch(domainnameOfRequest.toLowerCase(), list[i].toLowerCase())) {
                checkOK = true;
                break;
            }
        }
        if (!checkOK) {
            RcsAntiPhishViolateLog log = saveAntiPhishingRecordLog(AntiPhishType.DOMAINNAME_FUZZY, cust, req,
                    domainnameOfRequest, StringUtils.join(list, DELIMITER_DOMAINNAME_LIST), ird, null);
            logger.error("RcsAntiPhishingCheckService-反钓鱼-域名模糊匹配校验失败！！！报文中的域名:" + domainnameOfRequest + ", 合法域名列表:"
                    + StringUtils.join(list, ",") + ", 商户订单号:" + req.getMerOrderId());
            resp.setProcCode(ProcCodeEnum.PROC_CODE_200E5001.getCode());
            resp.setRickPhishingLogSq(log.getInfoSq());
            resp.setDicision(UserDicision.STOP.code);
            return resp;
        }
        return null;
    }

    private RcsAntiPhishViolateLog saveAntiPhishingRecordLog(AntiPhishType type, CustCorpInfo cust,
            RcsAntiPhishingRequest req, String actualValue, String legalValue, IllegalReqDicision ird, UserDicision ud) {
        RcsAntiPhishViolateLog log = new RcsAntiPhishViolateLog();
        Date t = new Date();
        log.setTransDtlSq(req.getTransDtlSq());
        log.setMerId(req.getCustId());
        log.setMerName(cust.getMerName());
        log.setMerOrderId(req.getMerOrderId());
        log.setMerTxnAmt(req.getMerTxnAmt());
        log.setMerTxnTm(req.getMerTxnTm());
        log.setAcctId(req.getAcctId());
        log.setAntiPhishType(type.code);
        log.setActualValue(actualValue);
        log.setLegalValue(legalValue);
        log.setIllegalReqDicision(ird.code);
        log.setUserDicision(null == ud ? "" : ud.code);
        log.setCreatedTs(t);
        log.setLastUpdTs(t);
        rcsAntiPhishingViolateLogManager.save(log);
        return log;
    }

    @Override
    public RcsAntiPhishingResponse doTimestampCheck(RcsAntiPhishingRequest req) throws GopayException {
        CustCorpInfo cust = custCorpInfoQueryManager.get(req.getCustId());
        return doTimestampCheck(cust, req);
    }

    public RcsAntiPhishingViolateLogManager getRcsAntiPhishingViolateLogManager() {
        return rcsAntiPhishingViolateLogManager;
    }

    public void setRcsAntiPhishingViolateLogManager(RcsAntiPhishingViolateLogManager rcsAntiPhishingViolateLogManager) {
        this.rcsAntiPhishingViolateLogManager = rcsAntiPhishingViolateLogManager;
    }

    public static void main(String[] args) {
        String[] list = { "aaa", "bbb" };
        System.out.println(StringUtils.join(list, DELIMITER_DOMAINNAME_LIST));
    }

    public RcsAntiPhishingGlobalManager getRcsAntiPhishingGlobalManager() {
        return rcsAntiPhishingGlobalManager;
    }

    public void setRcsAntiPhishingGlobalManager(RcsAntiPhishingGlobalManager rcsAntiPhishingGlobalManager) {
        this.rcsAntiPhishingGlobalManager = rcsAntiPhishingGlobalManager;
    }

    @Override
    public BufferedImage generateVerifyCodeImage(String randomStr, String merName, String tranAmt, String merReferUrl,
            String goodsInfo) {
        BufferedImage img = null;
        try {
            BufferedImage image = ImageIO.read(new File(FileUtil
                    .getCanonicalFilePath("classpath:data/AntiPhishVerifyCode.jpg")));

            int width = image.getWidth(null);
            int height = image.getHeight(null);
            img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // 设置图片大小的
            Graphics gra = img.getGraphics();
            gra.drawImage(image, 0, 0, null);
            gra.setColor(Color.black); // 设置字体色
            gra.setFont(new Font(null, Font.LAYOUT_LEFT_TO_RIGHT, 14));
            if (StringUtils.isNotEmpty(merName) && StringUtils.isNotEmpty(tranAmt)) {
                if (merName.length() > 20) {
                    gra.drawString(merName.substring(0, 20), 86, 31);
                    gra.drawString(merName.substring(20), 86, 60);
                } else {
                    gra.drawString(merName, 86, 31);
                }
                if (merReferUrl.length() > 40) {
                    gra.drawString(merReferUrl.substring(0, 40), 86, 86);
                    gra.drawString(merReferUrl.substring(40), 86, 111);
                } else {
                    gra.drawString(merReferUrl, 86, 86);
                }

                if (goodsInfo.length() > 20) {
                    gra.drawString(goodsInfo.substring(0, 20), 86, 136);
                    gra.drawString(goodsInfo.substring(20), 86, 152);
                } else {
                    gra.drawString(goodsInfo, 86, 136);
                }
                gra.drawString(tranAmt + "\uFF08\u5143\uFF09", 86, 229);

                // 验证码  update by dongdh 根据领导要求 去掉验证码 显示
                /*String c;
                Random random = new Random();
                gra.setFont(new Font(null, Font.PLAIN, 32));
                for (int i = 0; i < 4; i++) {
                    c = randomStr.substring(i, i + 1);
                    // 将认证码显示到图象中
                    gra.setColor(new Color(20 + random.nextInt(130), 20 + random.nextInt(130), 20 + random.nextInt(130)));
                    gra.drawString(c, (23 * i) + 140, 50);
                }*/
            }
        } catch (Exception e) {
            logger.error("生成反钓鱼图片验证码发生错误！", e);
        }
        return img;
    }

    @Override
    public BufferedImage getMerLogo(String custId, GatewayChannel gch) {
        String config = rcsAntiPhishingCustManager.getLegalValue(custId, AntiPhishType.VERIFY_CODE, gch);
        BufferedImage img = null;
        try {
            // 如果商户配置了logo，则取之；否则取国付宝logo。
            if (StringUtils.isBlank(config)) {
                config = "/data/logo.jpg";
                img = ImageIO.read(new File(FileUtil.getCanonicalFilePath(config)));
            } else {
                img = ImageIO.read(new File(config));
            }
        } catch (Exception e) {
            logger.error("反钓鱼-打开logo文件发生错误！" + custId + ", " + config, e);
        }
        return img;
    }

    public RcsAntiPhishingCustManager getRcsAntiPhishingCustManager() {
        return rcsAntiPhishingCustManager;
    }

    public void setRcsAntiPhishingCustManager(RcsAntiPhishingCustManager rcsAntiPhishingCustManager) {
        this.rcsAntiPhishingCustManager = rcsAntiPhishingCustManager;
    }

    @Override
    public String getMerLogoUrl(String custId, GatewayChannel gch) {
        return rcsAntiPhishingCustManager.getLegalValue(custId, AntiPhishType.VERIFY_CODE, gch);
    }
}
